//------------------------------------------------------------
//  Filename: vga_dma_ip_v1_0_M00_AXI.v
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2016-08-04 11:02
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module vga_dma_ip_v1_0_M00_AXI  #(
    parameter  C_M_TARGET_SLAVE_BASE_ADDR    = 32'h40000000,
    parameter integer C_M_AXI_BURST_LEN      = 64,
    parameter integer C_M_AXI_ID_WIDTH       = 1,
    parameter integer C_M_AXI_ADDR_WIDTH     = 32,
    parameter integer C_M_AXI_DATA_WIDTH     = 32,
    parameter integer C_M_AXI_AWUSER_WIDTH   = 0,
    parameter integer C_M_AXI_ARUSER_WIDTH   = 0,
    parameter integer C_M_AXI_WUSER_WIDTH    = 0,
    parameter integer C_M_AXI_RUSER_WIDTH    = 0,
    parameter integer C_M_AXI_BUSER_WIDTH    = 0
)( 
    //************* Write channel ***********
    input  wire                               M_AXI_ACLK,
    input  wire                               M_AXI_ARESETN,
    output wire [C_M_AXI_ID_WIDTH-1 : 0     ] M_AXI_AWID,
    output wire [C_M_AXI_ADDR_WIDTH-1 : 0   ] M_AXI_AWADDR,
    output wire [7 : 0                      ] M_AXI_AWLEN,
    output wire [2 : 0                      ] M_AXI_AWSIZE,
    output wire [1 : 0                      ] M_AXI_AWBURST,
    output wire                               M_AXI_AWLOCK,
    output wire [3 : 0                      ] M_AXI_AWCACHE,
    output wire [2 : 0                      ] M_AXI_AWPROT,
    output wire [3 : 0                      ] M_AXI_AWQOS,
    output wire [C_M_AXI_AWUSER_WIDTH-1 : 0 ] M_AXI_AWUSER,
    output wire                               M_AXI_AWVALID,
    input  wire                               M_AXI_AWREADY,
    output wire [C_M_AXI_DATA_WIDTH-1 : 0   ] M_AXI_WDATA,
    output wire [C_M_AXI_DATA_WIDTH/8-1 : 0 ] M_AXI_WSTRB,
    output wire                               M_AXI_WLAST,
    output wire [C_M_AXI_WUSER_WIDTH-1 : 0  ] M_AXI_WUSER,
    output wire                               M_AXI_WVALID,
    input  wire                               M_AXI_WREADY,
    input  wire [C_M_AXI_ID_WIDTH-1 : 0     ] M_AXI_BID,
    input  wire [1 : 0                      ] M_AXI_BRESP,
    input  wire [C_M_AXI_BUSER_WIDTH-1 : 0  ] M_AXI_BUSER,
    input  wire                               M_AXI_BVALID,
    output wire                               M_AXI_BREADY,
    //************* Read channel ***********
    output wire [C_M_AXI_ID_WIDTH-1 : 0     ] M_AXI_ARID,
    output reg  [C_M_AXI_ADDR_WIDTH-1 : 0   ] M_AXI_ARADDR,
    output wire [7 : 0                      ] M_AXI_ARLEN,
    output wire [2 : 0                      ] M_AXI_ARSIZE,
    output wire [1 : 0                      ] M_AXI_ARBURST,
    output wire                               M_AXI_ARLOCK,
    output wire [3 : 0                      ] M_AXI_ARCACHE,
    output wire [2 : 0                      ] M_AXI_ARPROT,
    output wire [3 : 0                      ] M_AXI_ARQOS,
    output wire [C_M_AXI_ARUSER_WIDTH-1 : 0 ] M_AXI_ARUSER,
    output reg                                M_AXI_ARVALID,
    input  wire                               M_AXI_ARREADY,
    input  wire [C_M_AXI_ID_WIDTH-1 : 0     ] M_AXI_RID,
    input  wire [C_M_AXI_DATA_WIDTH-1 : 0   ] M_AXI_RDATA,
    input  wire [1 : 0                      ] M_AXI_RRESP,
    input  wire                               M_AXI_RLAST,
    input  wire [C_M_AXI_RUSER_WIDTH-1 : 0  ] M_AXI_RUSER,
    input  wire                               M_AXI_RVALID,
    output wire                               M_AXI_RREADY,
    //************** fifo interface **********************
    input  wire                               fifo_rd_clk,
    input  wire                               fifo_rd_en,
    output wire                               fifo_empty,
    output wire [31:0                       ] fifo_dout,
    //************** config regs *************************
    input  wire [31:0                       ] start_addr,
    input  wire [31:0                       ] line_cnt,
    input  wire [31:0                       ] row_cnt,
    input  wire                               vga_frm_sync,
    input  wire                               mm_read_start,
    output reg                                mm_read_done
);  
//--------------------------------------------------------
wire clk = M_AXI_ACLK;
wire rst = ~M_AXI_ARESETN;

wire      line_last;
wire      frame_last;
wire      fifo_full;
//--------------------------------------------------------
parameter   IDLE        = 8'b0000_0001;
parameter   SEND_ADDR   = 8'b0000_0010;  
parameter   WAIT_DATA   = 8'b0000_0100;  
parameter   WAIT_FIFO   = 8'b0000_1000;  
parameter   NEXT_READ   = 8'b0001_0000;  
parameter   NEXT_LINE   = 8'b0010_0000;  
parameter   NEXT_FRAME  = 8'b0100_0000;  
//--------------------------------------------------------
reg [7:0]   cur_state;  
reg [7:0]   nxt_state;  
  
wire        prog_full;
wire        prog_empty;
//--------------------------------------------------------
reg vga_frm_sync_ff1;
reg vga_frm_sync_ff2;
always @(posedge clk) vga_frm_sync_ff1 <= vga_frm_sync;
always @(posedge clk) vga_frm_sync_ff2 <= vga_frm_sync_ff1;
//--------------------------------------------------------
reg new_frm;
always @(posedge clk) new_frm <= vga_frm_sync_ff2&(~vga_frm_sync_ff1); //neg edge
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        cur_state <= IDLE;  
    end  
    else  begin  
        cur_state <= nxt_state;  
    end  
end  
//--------------------------------------------------------
always @(*) begin  
    case(cur_state)  
        IDLE: begin  
            if(new_frm&mm_read_start)  
                nxt_state  = SEND_ADDR;  
            else  
                nxt_state  = cur_state;  
        end  
        SEND_ADDR: begin  
            if(M_AXI_ARREADY & M_AXI_ARVALID)  
                nxt_state  = WAIT_DATA;  
            else  
                nxt_state  = cur_state;  
        end  
        WAIT_DATA: begin  
            if(M_AXI_RREADY & M_AXI_RVALID & M_AXI_RLAST & prog_full)  
                nxt_state  = WAIT_FIFO;  
            else if(M_AXI_RREADY & M_AXI_RVALID & M_AXI_RLAST & (~line_last))  
                nxt_state  = NEXT_READ;  
            else if(M_AXI_RREADY & M_AXI_RVALID & M_AXI_RLAST & ( line_last) & (~frame_last))  
                nxt_state  = NEXT_LINE;  
            else if(M_AXI_RREADY & M_AXI_RVALID & M_AXI_RLAST & ( line_last) & ( frame_last))  
                nxt_state  = NEXT_FRAME;  
            else  
                nxt_state  = cur_state;  
        end  
        WAIT_FIFO: begin  
            if((~prog_full) & (~line_last))  
                nxt_state  = NEXT_READ;  
            else if(frame_last)  
                nxt_state  = NEXT_FRAME;  
            else if(prog_empty&line_last)  
                 nxt_state  = NEXT_LINE;   
            else  
                nxt_state  = cur_state;  
        end  
        NEXT_READ: begin  
            nxt_state  = SEND_ADDR;  
        end  
        NEXT_LINE: begin  
            nxt_state  = SEND_ADDR;  
        end  
        NEXT_FRAME: begin  
            nxt_state  = IDLE;//SEND_ADDR  
        end  
        default: begin  
            nxt_state  = cur_state;  
        end  
    endcase  
end  
//--------------------------------------------------------
assign M_AXI_RREADY = ((cur_state == IDLE)&&(prog_full))?1'b0:((cur_state==WAIT_DATA)?1'b1:1'b0);
//--------------------------------------------------------
reg[31:0] inner_frm_addr;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        inner_frm_addr <= 32'b0;    
    end 
    else if((cur_state == WAIT_DATA)&&(M_AXI_RVALID)&&(M_AXI_RREADY)) begin 
        inner_frm_addr <= inner_frm_addr + 32'b1;    
    end
    else if(new_frm)begin
        inner_frm_addr <= 32'b0;    
    end
end 
//--------------------------------------------------------
reg[31:0] inner_frm_line_cntr;
reg[31:0] inner_frm_row_cntr;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        inner_frm_line_cntr <= 32'b0;    
    end 
    else if((cur_state == WAIT_DATA)&&(M_AXI_RVALID)&&(M_AXI_RREADY)) begin   
        inner_frm_line_cntr <= inner_frm_line_cntr + 32'b1;    
    end 
    else if((cur_state == NEXT_LINE)||(cur_state == NEXT_FRAME)) begin
        inner_frm_line_cntr <= 32'b0;    
    end
end 
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        inner_frm_row_cntr <= 32'b0;    
    end 
    else if(cur_state == NEXT_LINE) begin   
        inner_frm_row_cntr <= inner_frm_row_cntr + 32'b1;    
    end 
    else if(cur_state == NEXT_FRAME) begin
        inner_frm_row_cntr <= 32'b0;    
    end
end 
//--------------------------------------------------------
reg[31:0] cur_start_addr;
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        cur_start_addr <= 32'h0;    
    end 
    else if(cur_state == IDLE) begin
        cur_start_addr <= start_addr;    
    end 
end 
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        M_AXI_ARADDR  <= 'b0;
        M_AXI_ARVALID <= 1'b0;
    end 
    else if(cur_state == SEND_ADDR) begin 
        M_AXI_ARADDR  <= cur_start_addr + {inner_frm_addr[29:0],2'b00};
        // Once asserted, VALIDs cannot be deasserted, so axi_arvalid
        // must wait until transaction is accepted  
        M_AXI_ARVALID <= (M_AXI_ARVALID&M_AXI_ARREADY)?1'b0:1'b1;
    end
    else begin
        M_AXI_ARVALID <= 1'b0;  
    end
end   
 
//--------------------------------------------------------
always @(posedge clk,posedge rst) begin
    if(rst)begin 
        mm_read_done <= 1'b0;        
    end 
    else if(new_frm) begin 
        mm_read_done <= 1'b0;        
    end 
    else if(cur_state == NEXT_FRAME) begin
        mm_read_done <= 1'b1;        
    end
end 
assign line_last  = ((inner_frm_line_cntr + 2) > line_cnt)?1'b1:1'b0;
assign frame_last = (line_last&&((inner_frm_row_cntr + 2) > row_cnt))?1'b1:1'b0;
wire   frm_data_valid = ~line_last; 

wire[31:0] fifo_din   = {frame_last,M_AXI_RDATA[30:0]};
wire       fifo_wr_en = (cur_state==WAIT_DATA)&&M_AXI_RVALID&&M_AXI_RREADY;
//--------------------------------------------------------
fifo_generator_v9_3 u_fifo_axi32bit (   
  .wr_clk        ( clk                ) ,
  .rd_clk        ( fifo_rd_clk        ) ,
  .din           ( fifo_din           ) ,
  .wr_en         ( fifo_wr_en         ) ,
  .rd_en         ( fifo_rd_en         ) ,
  .dout          ( fifo_dout          ) ,
  .full          ( fifo_full          ) ,
  .empty         ( fifo_empty         ) ,
  .prog_full     ( prog_full          ) ,
  .prog_empty    ( prog_empty         ) 
); 
//--------------------------------------------------------
assign M_AXI_ARID      = 6'b000000 ;
assign M_AXI_ARLEN     = 8'h3f     ;  //burst length: 64  
assign M_AXI_ARSIZE    = 3'b010    ;   //size: 4byte  
assign M_AXI_ARBURST   = 2'b01     ;   //00:fix,01:incr,10:wrap,11:rsv 
assign M_AXI_ARLOCK    = 2'b00     ;   //
assign M_AXI_ARCACHE   = 4'b0011   ;   /////  
assign M_AXI_ARPROT    = 3'b000    ;   // data trans
assign M_AXI_ARQOS     = 4'b0000   ;   //

endmodule


